import re
import os
import shutil
import subprocess
import time
import yaml
import threading
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input_dir", help="The directory for the import files", type=str, default=r"./input_voice")
parser.add_argument("-o", "--output_dir", help="The directory for the encoded files", type=str, default=r"./csgo")
parser.add_argument("-s", "--steam_dir", help="The directory for Steam",  type=str, default=r"D:/Software/Steam")
parser.add_argument("-c", "--csgo_dir", help="The directory for CSGO", type=str, default="")
parser.add_argument("-f", "--ffmpeg_dir", help="The directory for FFmpeg",  type=str, default="")
parser.add_argument("-b", "--bind", help="Bind the key",  type=str, default="v")
args = parser.parse_args()

wav_samplerate = 22050
wav_channels = 1
# 支持使用音频文件格式
suffix = ['wav', 'mp3', 'flac', 'm4a', 'mmg', 'aac']
input_dir = ''
output_dir = ''
steam_dir = ''
userdata_cfg_dir = []
csgo_dir = ''
cfg_dir = ''
ffmpeg_dir = ''
ffmpeg = ''
bind = ''
config_yml_file = ''
config_yml = {}
voice_input_wav_file = ''
sv_cfg_file = ''
sv_cfg = ''
sv_tracklist_cfg_file = ''
sv_relay_cfg_file = []

voices = []
state = 0 # 开始时为1

sv_cfg_temple = '''
alias sv_listtracks "exec sv_tracklist.cfg"
alias la sv_listtracks
alias sv_play sv_play_on
alias sv_play_on "alias sv_play sv_play_off; voice_inputfromfile 1; voice_loopback 1; +voicerecord"
alias sv_play_off "-voicerecord; voice_inputfromfile 0; voice_loopback 0; alias sv_play sv_play_on"
alias sv_updatecfg "host_writeconfig sv_relay"
bind $bind sv_play
$$$
voice_enable 1; voice_modenable 1; voice_forcemicrecord 0; con_enable 1
'''
sv_cfg = ''

sv_tracklist_cfg_temple = '''
echo "You can select tracks either by typing a tag, or their track number."
echo "--------------------Tracks--------------------"
$$$
echo "----------------------------------------------"
'''
sv_tracklist_cfg = ''


def init():
    global input_dir
    global output_dir
    global steam_dir
    global userdata_cfg_dir
    global csgo_dir
    global cfg_dir
    global ffmpeg_dir
    global ffmpeg
    global bind
    global config_yml_file
    global voice_input_wav_file
    global sv_cfg_file
    global sv_tracklist_cfg_file
    global sv_relay_cfg_file
    bind = args.bind[0]
    input_dir = args.input_dir
    output_dir = args.output_dir
    ffmpeg_dir = args.ffmpeg_dir
    steam_dir = args.steam_dir
    if args.csgo_dir:
        csgo_dir = args.csgo_dir
    else:
        csgo_dir = steam_dir + '/steamapps/common/Counter-Strike Global Offensive'
    # 将相对路径转绝对路径
    if not os.path.isabs(input_dir):
        input_dir = os.path.abspath(input_dir)
    if not os.path.isabs(output_dir):
        output_dir = os.path.abspath(output_dir)
    if not os.path.isabs(csgo_dir):
        csgo_dir = os.path.abspath(csgo_dir)
    # 配置目录和文件名
    config_yml_file = output_dir + '/config.yml'
    cfg_dir = csgo_dir + '/csgo/cfg'
    voice_input_wav_file = csgo_dir + '/voice_input.wav'
    sv_cfg_file = cfg_dir + '/sv.cfg'
    sv_tracklist_cfg_file = cfg_dir + '/sv_tracklist.cfg'
    for dir in os.listdir(steam_dir + '/userdata'):
        userdata_cfg_dir_ = f'{steam_dir}/userdata/{dir}/730/local/cfg'
        if os.path.exists(userdata_cfg_dir_):
            userdata_cfg_dir.append(userdata_cfg_dir_)
    if len(userdata_cfg_dir) == 0:
        raise Exception('userdata_cfg_dir not found.')
    sv_relay_cfg_file = [dir + '/sv_relay.cfg' for dir in userdata_cfg_dir]
    if not os.path.exists(input_dir):
        raise Exception('input directory not found.')
    if not os.path.exists(cfg_dir):
        raise Exception('csgo-cfg directory not found.')


def import_voice_files():
    global sv_cfg
    global config_yml
    global voices
    global ffmpeg_dir
    global ffmpeg
    # 检查是否有ffmpeg
    if ffmpeg_dir:
        if 'ffmpeg.exe' not in os.listdir(ffmpeg_dir):
            raise Exception('ffmpeg.exe not found.')
    elif os.path.exists('./ffmpeg.exe'):
        ffmpeg_dir = os.path.abspath('.')
    else:
        sys_path = os.environ['PATH'].split(';')
        ffmpeg_dir = [path for path in sys_path if os.path.exists(path) and 'ffmpeg.exe' in os.listdir(path)]
        if not len(ffmpeg_dir):
            raise Exception('ffmpeg.exe not found.')
        else:
            ffmpeg_dir = ffmpeg_dir[0]
    ffmpeg = ffmpeg_dir + '/ffmpeg.exe'
    # 导入音频文件
    input_files_basename = [file.split('.')[0] for file in os.listdir(input_dir) if file.split('.')[-1] in suffix]
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
        existed_files = []
    else:
        existed_files = [filename for filename in os.listdir(output_dir) if filename != 'config.yml']
    for file in existed_files:
        if file.split('.')[0] not in input_files_basename:
            os.remove(f'{output_dir}/{file}')
            print(f'remove {file}')
    existed_files_basename = [filename.split('.')[0] for filename in existed_files]
    input_files = [file for file in os.listdir(input_dir) if file.split('.')[-1] in suffix and file.split('.')[0] not in existed_files_basename]
    for file in input_files:
        basename = file.split('.')[0]
        remove_metadata = '-fflags +bitexact -flags:v +bitexact -flags:a +bitexact'
        cmd = f'{ffmpeg} -i "{input_dir}/{file}" -n -f wav -flags bitexact -map_metadata -1 -vn -acodec pcm_s16le {remove_metadata} -ar {wav_samplerate} -ac {wav_channels}  "{output_dir}/{basename}.wav"'
        subprocess.run(cmd, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
        print(f'add {basename}')
    voices = [filename.split('.')[0] for filename in os.listdir(output_dir) if filename != 'config.yml']
    
    if os.path.exists(config_yml_file):
        with open(config_yml_file) as f:
            config_yml = yaml.load(f.read(), Loader=yaml.FullLoader)


def set_cfg():
    global sv_tracklist_cfg
    global sv_cfg
    # 生成sv_tracklist.cfg文件
    sv_tracklist_cfg = sv_tracklist_cfg_temple
    for i, voice_name in enumerate(voices):
        if voice_name in config_yml.keys():
            line = f'echo "{i+1}. {voice_name} \tvolume:{config_yml[voice_name]}"\n'
        else:
            line = f'echo "{i+1}. {voice_name}"\n'
        index = sv_tracklist_cfg.find('$$$')
        sv_tracklist_cfg = sv_tracklist_cfg[:index] + line + sv_tracklist_cfg[index:]
    sv_tracklist_cfg = sv_tracklist_cfg.replace('$$$', '')
    with open(sv_tracklist_cfg_file, 'w', encoding='utf-8') as f:
        f.write(sv_tracklist_cfg)
    
    # 生成sv.cfg文件
    sv_cfg = sv_cfg_temple.replace('$bind', bind)
    for i, voice_name in enumerate(voices):
        line = f'alias {i+1} "bind = {i+1}; sv_updatecfg; echo Loaded: {voice_name}"\n'
        index = sv_cfg.find('$$$')
        sv_cfg = sv_cfg[:index] + line + sv_cfg[index:]
    sv_cfg = sv_cfg.replace('$$$', '')
    with open(sv_cfg_file, 'w', encoding='utf-8') as f:
        f.write(sv_cfg)


def proccess(cc):
    global state
    global bind
    # 识别空命令
    if not cc.strip():
        return
    c = cc.split(' ')
    if c[0] == 'exit':
        stop_event.set() # 停止work函数线程
        proccess('stop')
        exit()
    elif c[0] == 'start':
        if state:
            print('Already start.')
            return
        state = 1
        set_cfg()
    elif c[0] == 'stop':
        if not state:
            print('Already stop.')
            return
        state = 0
        stop_event.set()
        if os.path.exists(voice_input_wav_file):
            # cmd = f'rm "{voice_input_wav_file}"'
            # print(cmd)
            # subprocess.run(cmd, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
            os.remove(voice_input_wav_file)
        if os.path.exists(sv_tracklist_cfg_file):
            # cmd = f'rm "{sv_tracklist_cfg_file}"'
            # print(cmd)
            # subprocess.run(cmd, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
            os.remove(sv_tracklist_cfg_file)
        if os.path.exists(sv_cfg_file):
            # cmd = f'rm "{sv_cfg_file}"'
            # print(cmd)
            # subprocess.run(cmd, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
            os.remove(sv_cfg_file)
    elif c[0] == 'bind':
        if 1 < len(c):
            bind = c[1][0]
        else:
            print('Please input which key you bind.')
    elif c[0] == 'echo':
        if 1 < len(c):
            text = cc[5:].strip()
            text = text.strip('"')
            text = text.strip("'")
            print(text)
    elif c[0] == 'la':
        c_list = sv_tracklist_cfg.split('\n')
        for c_ in c_list:
            proccess(c_)
    elif c[0] == 'set':
        if 3 <= len(c):
            index = int(c[1])
            ratio = float(c[2])
            if ratio == 1.0:
                if voices[index-1] in config_yml.keys():
                    del config_yml[voices[index-1]]
            else:
                config_yml[voices[index-1]] = ratio
            with open(config_yml_file, 'w') as f:
                f.write(yaml.dump(config_yml))
            set_cfg()
            
    # 播放的音频索引
    elif cc.isdigit():
        if os.path.exists(voice_input_wav_file):
            os.remove(voice_input_wav_file)
        index = int(cc) 
        if 1 <= index <= len(voices):
            if voices[index-1] in config_yml.keys():
                remove_metadata = '-fflags +bitexact -flags:v +bitexact -flags:a +bitexact'
                cmd = f'{ffmpeg} -i "{output_dir}/{voices[index-1]}.wav" -filter:a "volume={config_yml[voices[index-1]]}" {remove_metadata} "{voice_input_wav_file}"'
                subprocess.run(cmd, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
            else:
                shutil.copyfile(f'{output_dir}/{voices[index-1]}.wav', voice_input_wav_file)
            print(f'Loaded: {voices[index-1]}')

    else:
        print(f'No cmd: {c}')


def work(stop_event):
    global state
    while not stop_event.is_set():
        time.sleep(0.5)
        if state:
            for sv_relay in sv_relay_cfg_file:
                if os.path.exists(sv_relay):
                    with open(sv_relay, encoding='utf-8') as f:
                        sv_relay_cfg_text = f.read()
                        line = re.findall('bind "=" "\d+"', sv_relay_cfg_text)[0]
                        index = line[10:-1]
                        print(index)
                        proccess(index)
                        print('> ')
                    # cmd = f'rm "{sv_relay}"'
                    # print(cmd)
                    # subprocess.run(cmd, stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
                    os.remove(sv_relay)


def voice_updata(stop_event):
    recent = os.listdir(input_dir)
    while not stop_event.is_set():
        time.sleep(2.0)
        recent_ = os.listdir(input_dir)
        if recent != recent_:
            import_voice_files()
            set_cfg()
            print('> ')
            recent = recent_



if __name__ == '__main__':
    init()
    import_voice_files()
    stop_event = threading.Event() # 在执行exit命令时设置，用于停止work函数线程
    threading.Thread(target=work, args=(stop_event, )).start()
    threading.Thread(target=voice_updata, args=(stop_event, )).start()
    print('> start')
    proccess('start')
    while True:
        c = input('> ')
        proccess(c)
    
